package com.igoal.dao;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.igoal.entity.ActionTypeEnum;
import com.igoal.entity.ComponentEnum;
import com.igoal.entity.DataStatus;
import com.igoal.entity.PrivacyEnum;
import com.igoal.entity.PrivacySetting;
import com.igoal.entity.User;
import com.igoal.entity.UserFriend;
import com.igoal.entity.WallUpdate;
import com.igoal.entity.factory.EntityFactory;
import com.igoal.web.Constants;

@Repository("wallUpdateDAO")
public class WallUpdateDAOImpl extends GenericDaoHibernate<WallUpdate, Long> implements
		WallUpdateDAO {

	private PrivacySettingDAO privacySettingDAO;

	public WallUpdateDAOImpl() {
		super(WallUpdate.class);
	}

	@Autowired
	public void setPrivacySettingDAO(PrivacySettingDAO privacySettingDAO) {
		this.privacySettingDAO = privacySettingDAO;
	}

	@Override
	public WallUpdate save(WallUpdate object) {
		object.setUpdateDate(new Date());
		return super.save(object);
	}

	public void updateToWall(User componentOwner, ComponentEnum component, Long componentId,
			ActionTypeEnum action, PrivacySetting privacy) {
		DetachedCriteria dc = DetachedCriteria.forClass(WallUpdate.class);
		dc.add(Restrictions.eq("componentOwner", componentOwner));
		dc.add(Restrictions.eq("component", component));
		dc.add(Restrictions.eq("componentId", componentId));

		WallUpdate wu = null;
		List<WallUpdate> result = findByCriteria(dc);
		if (result == null || result.size() > 0) {
			wu = result.get(0);
			// don't change privacy in this case
		} else {
			wu = EntityFactory.createWallUpdate(componentOwner, component, componentId);
			wu.setPrivacy(privacy);
		}
		
		if (action != null) {
			wu.setAction(action);
		} else if (wu.getAction() == null) {
			switch (component) {
				case GOAL_ENTRY:
					wu.setAction(ActionTypeEnum.NEW);
					break;
				case DONE_ENTRY:
					wu.setAction(ActionTypeEnum.NEW);
					break;
				default:
					break;
			}
		}
		
		if (wu.getAction() != null) {
			save(wu);
		}
	}

	public List<WallUpdate> getWallActivities(User viewer, User wallOwner, Date createBefore) {
		PrivacySetting publicPrivacy = privacySettingDAO.getPrivacy(PrivacyEnum.PUBLIC);
		PrivacySetting friendPrivacy = privacySettingDAO.getPrivacy(PrivacyEnum.FRIEND);

		List<PrivacySetting> friendPrivacies = new ArrayList<PrivacySetting>();
		friendPrivacies.add(publicPrivacy);
		friendPrivacies.add(friendPrivacy);

		DetachedCriteria dc = DetachedCriteria.forClass(WallUpdate.class);

		if (viewer == null) {
			// anonymous login
			return null;
		} else if (viewer.equals(wallOwner)) {
			// my wall
			Set<UserFriend> ufs = viewer.getFriends();
			Set<User> friends = new HashSet<User>();
			for (UserFriend uf : ufs) {
				friends.add(uf.getFrom());
			}
			Criterion e1 = Restrictions.eq("componentOwner", viewer);
			if (friends.size() > 0) {
				Criterion e2 = Restrictions.and(Restrictions.in("componentOwner", friends),
						Restrictions.in("privacy", friendPrivacies));
				dc.add(Restrictions.or(e1, e2));
			} else {
				dc.add(e1);
			}
		} else {
			// friend wall
			dc.add(Restrictions.eq("componentOwner", wallOwner));
			if (wallOwner.getFriends().contains(viewer)) {
				dc.add(Restrictions.in("privacy", friendPrivacies));
			} else {
				dc.add(Restrictions.eq("privacy", publicPrivacy));
			}
		}

		if (createBefore != null) {
			dc.add(Restrictions.lt("createDate", createBefore));
		}
		dc.addOrder(Order.desc("createDate"));
		getByStatus(dc, DataStatus.USING);

		return findByCriteria(dc, 0, Constants.WALL_ACTIVITIES_PAGE_SIZE);
	}

	public void removeFromWall(ComponentEnum component, Long componentId) {
		DetachedCriteria dc = DetachedCriteria.forClass(WallUpdate.class);
		dc.add(Restrictions.eq("component", component));
		dc.add(Restrictions.eq("componentId", componentId));
		List<WallUpdate> result = findByCriteria(dc);

		for (WallUpdate wu : result) {
			wu.setStatus(DataStatus.DELETED);
			save(wu);
		}
	}
	
}
